﻿' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

Imports System.Collections.Immutable
Imports System.Globalization
Imports System.Threading
Imports Microsoft.CodeAnalysis

Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
    ''' <summary>
    ''' Represents a non-element field of a tuple type (such as (int, byte).Rest)
    ''' that is backed by a real field within the tuple underlying type.
    ''' </summary>
    Friend Class TupleFieldSymbol
        Inherits WrappedFieldSymbol

        Protected ReadOnly _containingTuple As TupleTypeSymbol

        ''' <summary>
        ''' If this field represents a tuple element with index X, the field contains
        '''  2X      if this field represents a Default-named element
        '''  2X + 1  if this field represents a Friendly-named element
        ''' Otherwise, (-1 - [index in members array]);
        ''' </summary>
        Private ReadOnly _tupleElementIndex As Integer

        Public Overrides ReadOnly Property IsTupleField As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides ReadOnly Property TupleUnderlyingField As FieldSymbol
            Get
                Return Me._underlyingField
            End Get
        End Property

        ''' <summary>
        ''' If this is a field representing a tuple element,
        ''' returns the index of the element (zero-based).
        ''' Otherwise returns -1
        ''' </summary>
        Public Overrides ReadOnly Property TupleElementIndex As Integer
            Get
                If _tupleElementIndex < 0 Then
                    Return -1
                End If

                Return _tupleElementIndex >> 1
            End Get
        End Property

        Public Overrides ReadOnly Property IsDefaultTupleElement As Boolean
            Get
                ' not negative and even
                Return (_tupleElementIndex And ((1 << 31) Or 1)) = 0
            End Get
        End Property

        Public Overrides ReadOnly Property AssociatedSymbol As Symbol
            Get
                Return Me._containingTuple.GetTupleMemberSymbolForUnderlyingMember(Of Symbol)(Me._underlyingField.AssociatedSymbol)
            End Get
        End Property

        Public Overrides ReadOnly Property ContainingSymbol As Symbol
            Get
                Return Me._containingTuple
            End Get
        End Property

        Public Overrides ReadOnly Property CustomModifiers As ImmutableArray(Of CustomModifier)
            Get
                Return Me._underlyingField.CustomModifiers
            End Get
        End Property

        Public Overrides ReadOnly Property Type As TypeSymbol
            Get
                Return Me._underlyingField.Type
            End Get
        End Property

        Public Sub New(container As TupleTypeSymbol, underlyingField As FieldSymbol, tupleElementIndex As Integer)
            MyBase.New(underlyingField)

            Debug.Assert(container.UnderlyingNamedType.IsSameTypeIgnoringAll(underlyingField.ContainingType) OrElse TypeOf Me Is TupleVirtualElementFieldSymbol,
                                            "virtual fields should be represented by " + NameOf(TupleVirtualElementFieldSymbol))

            _containingTuple = container
            _tupleElementIndex = tupleElementIndex
        End Sub

        Public Overrides Function GetAttributes() As ImmutableArray(Of VisualBasicAttributeData)
            Return Me._underlyingField.GetAttributes()
        End Function

        Friend Overrides Function GetUseSiteErrorInfo() As DiagnosticInfo
            Dim useSiteDiagnostic As DiagnosticInfo = MyBase.GetUseSiteErrorInfo
            MyBase.MergeUseSiteErrorInfo(useSiteDiagnostic, Me._underlyingField.GetUseSiteErrorInfo())
            Return useSiteDiagnostic
        End Function

        Public Overrides Function GetHashCode() As Integer
            Return Hash.Combine(_containingTuple.GetHashCode(), _tupleElementIndex.GetHashCode())
        End Function

        Public Overrides Function Equals(obj As Object) As Boolean
            Return Me.Equals(TryCast(obj, TupleFieldSymbol))
        End Function

        Public Overloads Function Equals(other As TupleFieldSymbol) As Boolean
            Return other IsNot Nothing AndAlso
                _tupleElementIndex = other._tupleElementIndex AndAlso
                TypeSymbol.Equals(_containingTuple, other._containingTuple, TypeCompareKind.ConsiderEverything)
        End Function
    End Class

    ''' <summary>
    ''' Represents an element field of a tuple type (such as (int, byte).Item1)
    ''' that is backed by a real field with the same name within the tuple underlying type.
    ''' </summary>
    Friend Class TupleElementFieldSymbol
        Inherits TupleFieldSymbol

        Private ReadOnly _locations As ImmutableArray(Of Location)

        ' default tuple elements like Item1 Or Item20 could be provided by the user or
        ' otherwise implicitly declared by compiler
        Private ReadOnly _isImplicitlyDeclared As Boolean

        Private ReadOnly _correspondingDefaultField As TupleElementFieldSymbol

        Public Sub New(container As TupleTypeSymbol,
                       underlyingField As FieldSymbol,
                       tupleElementIndex As Integer,
                       location As Location,
                       isImplicitlyDeclared As Boolean,
                       correspondingDefaultFieldOpt As TupleElementFieldSymbol)

            MyBase.New(container, underlyingField, If(correspondingDefaultFieldOpt Is Nothing, tupleElementIndex << 1, (tupleElementIndex << 1) + 1))

            Me._locations = If((location Is Nothing), ImmutableArray(Of Location).Empty, ImmutableArray.Create(Of Location)(location))
            Me._isImplicitlyDeclared = isImplicitlyDeclared

            Debug.Assert(correspondingDefaultFieldOpt Is Nothing = Me.IsDefaultTupleElement)
            Debug.Assert(correspondingDefaultFieldOpt Is Nothing OrElse correspondingDefaultFieldOpt.IsDefaultTupleElement)

            _correspondingDefaultField = If(correspondingDefaultFieldOpt, Me)
        End Sub

        Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
            Get
                Return Me._locations
            End Get
        End Property

        Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
            Get
                Return If(_isImplicitlyDeclared,
                    ImmutableArray(Of SyntaxReference).Empty,
                    GetDeclaringSyntaxReferenceHelper(Of VisualBasicSyntaxNode)(_locations))
            End Get
        End Property

        Public Overrides ReadOnly Property IsImplicitlyDeclared As Boolean
            Get
                Return _isImplicitlyDeclared
            End Get
        End Property

        Friend Overrides ReadOnly Property TypeLayoutOffset As Integer?
            Get
                Dim flag As Boolean = Me._underlyingField.ContainingType IsNot Me._containingTuple.TupleUnderlyingType
                Dim result As Integer?
                If flag Then
                    result = Nothing
                Else
                    result = MyBase.TypeLayoutOffset
                End If
                Return result
            End Get
        End Property

        Public Overrides ReadOnly Property AssociatedSymbol As Symbol
            Get
                Dim flag As Boolean = Me._underlyingField.ContainingType IsNot Me._containingTuple.TupleUnderlyingType
                Dim result As Symbol
                If flag Then
                    result = Nothing
                Else
                    result = MyBase.AssociatedSymbol
                End If
                Return result
            End Get
        End Property

        Public Overrides ReadOnly Property CorrespondingTupleField As FieldSymbol
            Get
                Return _correspondingDefaultField
            End Get
        End Property
    End Class

    ''' <summary>
    ''' Represents an element field of a tuple type (such as (int a, byte b).a, or (int a, byte b).b)
    ''' that is backed by a real field with a different name within the tuple underlying type.
    ''' </summary>
    Friend NotInheritable Class TupleVirtualElementFieldSymbol
        Inherits TupleElementFieldSymbol

        Private ReadOnly _name As String
        Private ReadOnly _cannotUse As Boolean ' With LanguageVersion 15, we will produce named elements that should not be used

        Public Sub New(container As TupleTypeSymbol,
                       underlyingField As FieldSymbol,
                       name As String,
                       cannotUse As Boolean,
                       tupleElementOrdinal As Integer,
                       location As Location,
                       isImplicitlyDeclared As Boolean,
                       correspondingDefaultFieldOpt As TupleElementFieldSymbol)

            MyBase.New(container, underlyingField, tupleElementOrdinal, location, isImplicitlyDeclared, correspondingDefaultFieldOpt)

            Debug.Assert(name <> Nothing)
            Debug.Assert(name <> underlyingField.Name OrElse Not container.UnderlyingNamedType.Equals(underlyingField.ContainingType),
                                "fields that map directly to underlying should not be represented by " + NameOf(TupleVirtualElementFieldSymbol))

            Me._name = name
            Me._cannotUse = cannotUse
        End Sub

        Friend Overrides Function GetUseSiteErrorInfo() As DiagnosticInfo
            If _cannotUse Then
                Return ErrorFactory.ErrorInfo(ERRID.ERR_TupleInferredNamesNotAvailable, _name,
                                              New VisualBasicRequiredLanguageVersion(LanguageVersion.VisualBasic15_3))
            End If

            Return MyBase.GetUseSiteErrorInfo()
        End Function

        Public Overrides ReadOnly Property Name As String
            Get
                Return Me._name
            End Get
        End Property

        Friend Overrides ReadOnly Property TypeLayoutOffset As Integer?
            Get
                Return Nothing
            End Get
        End Property

        Public Overrides ReadOnly Property AssociatedSymbol As Symbol
            Get
                Return Nothing
            End Get
        End Property

        Public Overrides ReadOnly Property IsVirtualTupleField As Boolean
            Get
                Return True
            End Get
        End Property
    End Class
End Namespace
